package Model.Gates;

import Model.Main.Wire;
import java.util.ArrayList;
import java.util.logging.Level;

/**
 * Kompozit osztály.
 * Tartaalmazhat gate-eket.
 * @author citromos ezustfoka
 */
public final class Composite extends Gate {

    private ArrayList<Gate> gates;
    private ArrayList<CompPort> inPort;
    private ArrayList<CompPort> outPort;

    public Composite(String name) {
        this.name = name;
        maxInputs = 2;
        maxOutputs = 2;

        inPort = new ArrayList<CompPort>();
        outPort = new ArrayList<CompPort>();
        gates = new ArrayList<Gate>();
        this.reset();

        logger.log(Level.FINEST, "{0} created with {1} input(s).",
                new Object[]{this, maxInputs});
    }

    /**
     * Inputok és outputok száma.
     * @param maxi
     * @param maxo
     */
    public Composite(int maxi, int maxo, String name) {
        this.name = name;
        maxInputs = maxi;
        maxOutputs = maxo;

        inPort = new ArrayList<CompPort>();
        outPort = new ArrayList<CompPort>();
        gates = new ArrayList<Gate>();
        this.reset();
        logger.log(Level.CONFIG, "" + this.getClass().getName() + " " + this.getName() + " created with " + maxInputs + " input(s) and with " + maxOutputs + " output(s)");
        /**
         * Inicializáljuk az inputsWired listát.
         */
        initInputsWired();
    }

    /**
     * Az elem egy kimenetére csatlakoztatott vezeték beállítása.
     * @param wire A kimenetre kötni kívánt vezeték.
     * @param inputNumber Melyik kimenetre megy?
     * !!!!!!!!!!!!!!!!!!!!!Minden gyermekosztálynak felül kell definiálnia!!!!!
     * @return
     */
    @Override
    public void setOutputWire(Wire w, int outputNumber) {
        outPort.get(outputNumber).setOutputWire(w, 0);

    }

    @Override
    public void setInput(int inputNumber, Boolean val) throws Exception {
        inPort.get(inputNumber).setInput(0, val);
    }

    /**
     * Visszaadja n. kaput.
     * @param n
     * @return
     */
    Gate getGate(int n) {
        logger.log(Level.CONFIG, "Returning " + n + ". gate from " + this.getClass().getName() + " " + this.getName() + "");
        return gates.get(n);
    }

    /**
     * Alaphelyzetbe állítja a kapuk értékeit, nem lesz stabil semmi.
     */
    void clear() {
        gates.clear();
        logger.log(Level.CONFIG, "Cleared all gates in " + this.getClass().getName() + " " + this.getName() + "");

    }

    /**
     * Hozzáad egy kaput
     * @param g
     */
    public void addGate(Gate g) {
        gates.add(g);
        logger.log(Level.CONFIG, "Gate " + g.getClass().getName() + " " + g.getName() + " has been added to " + this.getClass().getName() + " " + this.getName() + "");
    }

    /**
     * Valamelyik wire-hez gyerekwire-t add.
     * @param parent
     * @param child
     * @throws Exception
     */
    void addChildWire(Wire parent, Wire child) throws Exception {
        parent.addChildWire(child);
    }

    /**
     * Törli a gyerekwire-t
     * @param parent
     * @param child
     * @throws Exception
     */
    void delChildWire(Wire parent, Wire child) throws Exception {
        parent.delChildWire(child);
    }

    /**
     * A kompozit bemenetét köti össze egy belső kapu bemenetével.
     * @param fromInput
     * @param to
     * @param inputNumber
     */
    public void addWire(int fromInput, Gate to, int inputNumber, String name) throws Exception {
        Wire w = new Wire(to, inputNumber, name);
        inPort.get(fromInput).setOutputWire(w, 0);
    }

    /**
     * Egy belső kapu kimenetét köti össze a kompozit kimenetével.
     * @param from
     * @param outputNumber
     * @param toInput
     */
    public void addWire(Gate from, int outputNumber, int toOutput, String name) throws Exception {
        Wire w = new Wire(outPort.get(toOutput), 0, name);
        from.setOutputWire(w, outputNumber);
    }

    /**
     * Minden változó alaphelyzetbe állítása.
     * @return
     *
     */
    @Override
    public void reset() {
        logger.log(Level.FINEST, "Resetting " + this.getClass().getName() + " " + this.getName() + " to default values.");
        // alap változók
        inputs = new ArrayList<Boolean>();
        inputsStable = new ArrayList<Boolean>();
        // berendezem az inputokat default értékekre
        for (int i = 0; i < this.maxInputs; i++) {
            CompPort input = new CompPort();
            inPort.add(input);
        }
        for (int i = 0; i < this.maxOutputs; i++) {
            CompPort output = new CompPort();
            outPort.add(output);
        }


    }

    /**
     *
     * @param outputNumber
     * @return
     * @throws exception ha outputNumber != 0
     */
    @Override
    public Wire getWire(int outputNumber) throws Exception {
        if (outputNumber >= maxOutputs) {
            throw new Exception("Haven't got so much output");
        }
        return (outPort.get(outputNumber).getWire(0));
    }

    public int getMaxOutput() {
        return (this.maxOutputs);
    }

    public Gate getCompGates(int i) {
        return (this.gates.get(i));
    }

    public int getCompSize() {
        return (this.gates.size());
    }

    public Gate getCompInPort(int i) {
        return (this.inPort.get(i));
    }

    public Gate getCompOutPort(int i) {
        return (this.outPort.get(i));
    }
}
